package com.keensense.vcm.internal;

import com.keensense.vcm.ClientException;
import com.keensense.vcm.HttpMethod;
import com.keensense.vcm.ServiceException;
import com.keensense.vcm.VCMException;
import com.keensense.vcm.common.auth.CredentialsProvider;
import com.keensense.vcm.common.comm.*;
import com.keensense.vcm.common.parser.*;
import com.keensense.vcm.common.utils.ExceptionFactory;
import com.keensense.vcm.common.utils.LogUtils;
import com.keensense.vcm.model.VCMResponse;

import java.net.URI;
import java.util.List;

import static com.keensense.vcm.common.utils.LogUtils.logException;
import static com.keensense.vcm.common.utils.VCMUtils.safeCloseResponse;
import static com.keensense.vcm.internal.VCMConstants.DEFAULT_CHARSET_NAME;

/**
 * @author lanjian
 * @date 2020/4/29
 */
public abstract class VCMOperation {

    protected volatile URI endpoint;
    protected CredentialsProvider credentialsProvider;
    protected ServiceClient client;

    protected static VCMErrorResponseHandler errorResponseHandler = new VCMErrorResponseHandler();
    protected static ResponseParsers.GlobalResponseParser<VCMResponse> emptyResponseParser = new ResponseParsers.GlobalResponseParser(VCMResponse.class);
    protected static RetryStrategy noRetryStrategy = new NoRetryStrategy();

    protected VCMOperation(ServiceClient client, CredentialsProvider credsProvider) {
        this.client = client;
        this.credentialsProvider = credsProvider;
    }

    public URI getEndpoint() {
        return endpoint;
    }

    public void setEndpoint(URI endpoint) {
        this.endpoint = URI.create(endpoint.toString());
    }

    protected ServiceClient getInnerClient() {
        return this.client;
    }

    protected ResponseMessage send(RequestMessage request, ExecutionContext context)
            throws VCMException, ClientException {
        return send(request, context, false);
    }

    protected ResponseMessage send(RequestMessage request, ExecutionContext context, boolean keepResponseOpen)
            throws VCMException, ClientException {
        ResponseMessage response = null;
        try {
            response = client.sendRequest(request, context);
            return response;
        } catch (ServiceException e) {
            assert (e instanceof VCMException);
            throw (VCMException) e;
        } finally {
            if (response != null && !keepResponseOpen) {
                safeCloseResponse(response);
            }
        }
    }

    protected <T> T doOperation(RequestMessage request, ResponseParser<T> parser) throws VCMException, ClientException {

        return doOperation(request, parser, false);
    }

    protected <T> T doOperation(RequestMessage request, ResponseParser<T> parser, boolean keepResponseOpen)
            throws VCMException, ClientException {
        return doOperation(request, parser, keepResponseOpen, null);
    }

    protected <T> T doOperation(RequestMessage request, ResponseParser<T> parser, boolean keepResponseOpen,
                                List<ResponseHandler> reponseHandlers) throws VCMException, ClientException {
        request.getHeaders().putAll(client.getClientConfiguration().getDefaultHeaders());

        ExecutionContext context = createDefaultContext(request.getMethod());

        if (reponseHandlers != null) {
            for (ResponseHandler handler : reponseHandlers) {
                context.addResponseHandler(handler);
            }
        }
        ResponseMessage response = send(request, context, keepResponseOpen);
        try {
            return parser.parse(response);
        } catch (ResponseParseException rpe) {
            VCMException oe =
                    ExceptionFactory.createInvalidResponseException(response.getRequestId(), rpe.getMessage(), rpe);
            logException("Unable to parse response error: ", rpe);
            throw oe;
        }
    }

    protected ExecutionContext createDefaultContext(HttpMethod method) {
        ExecutionContext context = new ExecutionContext();
        context.setCharset(DEFAULT_CHARSET_NAME);
        context.addResponseHandler(errorResponseHandler);
        if (method == HttpMethod.POST) {
            context.setRetryStrategy(noRetryStrategy);
        }
        context.setCredentials(credentialsProvider.getCredentials());
        return context;
    }

    /**
     * 格式化参数
     *
     * @param object
     * @param clazz
     * @return
     */
    protected String getXmlFromObject(Object object, Class clazz) {
        try {
            JAXBRequestParser requestParser = new JAXBRequestParser(clazz);
            String xml = requestParser.parse(object);
            LogUtils.getLog().info(clazz.getName() + " request parameter:" + xml);
            return xml;
        } catch (RequestParseException e) {
            throw new ClientException(e);
        }
    }

}
